package org.dromara.northstar.gateway.okx;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.okex.open.api.config.APIConfiguration;
import com.okex.open.api.service.account.impl.AccountAPIServiceImpl;
import com.okex.open.api.service.marketData.impl.MarketDataAPIServiceImpl;
import com.okex.open.api.service.publicData.impl.PublicDataAPIServiceImpl;
import org.dromara.northstar.common.IDataSource;
import org.dromara.northstar.common.ObjectManager;
import org.dromara.northstar.common.constant.ChannelType;
import org.dromara.northstar.common.event.NorthstarEventType;
import org.dromara.northstar.common.model.Identifier;
import org.dromara.northstar.common.model.core.Bar;
import org.dromara.northstar.common.model.core.Contract;

import java.time.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

import lombok.extern.slf4j.Slf4j;
import org.dromara.northstar.common.model.core.Notice;
import org.dromara.northstar.common.utils.CommonUtils;
import org.dromara.northstar.gateway.Gateway;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import xyz.redtorch.pb.CoreEnum;

@Slf4j
public class OkxDataServiceManager implements IDataSource {
    @Autowired
    private OkxGatewaySettings settings;
    @Autowired
    private ObjectManager<Gateway> gatewayManager;

    private APIConfiguration config = new APIConfiguration();

    private ExecutorService exec;
    private final int REQUEST_LIMIT = 18; // 请求限制
    private final long RATE_LIMIT_PERIOD = 5; // 限速周期（秒）

    public OkxDataServiceManager() {
        exec = Executors.newCachedThreadPool(); // 使用可缓存的线程池

    }

    @Override
    public List<Bar> getMinutelyData(Contract contract, LocalDate startDate, LocalDate endDate) {
        List<Bar> allData = new ArrayList<>();

        LocalDateTime startTime = startDate.atStartOfDay();
        LocalDateTime endTime = endDate.atTime(LocalTime.MAX);

        long minutes = Duration.between(startTime, endTime).toMinutes();
        AtomicInteger requestCounter = new AtomicInteger(0);
        //按照300分钟进行分割
        for (int i = 0; i <= minutes; i += 100) {
            LocalDateTime currentStartTime = startTime.plusMinutes(i).minusMinutes(1);
            LocalDateTime currentEndTime;
            long l = minutes - i;
            //最后不满1000分钟则取尾数
            if (l < 100) {
                currentEndTime = currentStartTime.plusMinutes(l);
            } else {
                currentEndTime = currentStartTime.plusMinutes(101);
            }

            Instant instantStart = currentStartTime.atZone(ZoneId.of("Asia/Shanghai")).toInstant();
            Instant instantEnd = currentEndTime.atZone(ZoneId.of("Asia/Shanghai")).toInstant();

            // 检查请求次数是否达到限制
            int currentCount = requestCounter.incrementAndGet();
            if (currentCount >= REQUEST_LIMIT) {
                try {
                    log.info("请求次数超过限制，休眠{}秒", RATE_LIMIT_PERIOD);
                    TimeUnit.SECONDS.sleep(RATE_LIMIT_PERIOD);
                    requestCounter.set(0); // 休眠后重置计数器
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            exec.execute(() -> {
                List<Bar> data = getHistoricalData(contract, instantStart.toEpochMilli(), instantEnd.toEpochMilli(), "1m");
                allData.addAll(data);
            });
        }
        allData.sort(Comparator.comparing(Bar::actionTimestamp).reversed());
        return allData;
    }

    @Override
    public List<Bar> getQuarterlyData(Contract contract, LocalDate startDate, LocalDate endDate) {
        return getHistoricalData(contract, startDate.atStartOfDay().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(), endDate.atTime(LocalTime.MAX).atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(), "15m");
    }

    @Override
    public List<Bar> getHourlyData(Contract contract, LocalDate startDate, LocalDate endDate) {
        return getHistoricalData(contract, startDate.atStartOfDay().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(), endDate.atTime(LocalTime.MAX).atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(), "1h");
    }

    @Override
    public List<Bar> getDailyData(Contract contract, LocalDate startDate, LocalDate endDate) {
        return getHistoricalData(contract, startDate.atStartOfDay().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(), endDate.atTime(LocalTime.MAX).atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(), "1d");
    }

    @Override
    public List<LocalDate> getHolidays(ChannelType exchange, LocalDate startDate, LocalDate endDate) {
        return Collections.emptyList();
    }

    @Override
    public List<Contract> getAllContracts() {
        LinkedList<Contract> resultList = new LinkedList<>();
        config = getAPIConfiguration();
        //交易规则和交易对
        PublicDataAPIServiceImpl accountAPIService = new PublicDataAPIServiceImpl(config);
        JSONObject result = accountAPIService.getInstruments("SWAP", "", "", "");
        JSONArray data = result.getJSONArray("data");
        for (int i = 0; i < data.size(); i++) {
            JSONObject obj = data.getJSONObject(i);
            OkxContract contract = new OkxContract(obj, this);
            resultList.add(contract.contract());
        }
        return resultList;
    }

    private APIConfiguration getAPIConfiguration() {
        config.setDomain("https://www.okx.com/");
        //secretKey,api注册成功后页面上有
        config.setApiKey(settings.getApiKey());
        config.setSecretKey(settings.getSecretKey());
        //Passphrase忘记后无法找回
        config.setPassphrase(settings.getPassphrase());
        //请求模拟盘的接口需要传入1，否则传入0
        config.setxSimulatedTrading(settings.getAccountType());

        return config;
    }

    public List<Bar> getHistoricalData(Contract contract, long startDate, long endDate, String interval) {
        log.debug("历史行情{}数据：{}，{} -> {}", interval, contract.unifiedSymbol(), startDate, endDate);
        Gateway gateway = gatewayManager.get(Identifier.of(ChannelType.OKX.toString()));
        if (gateway == null) {
            throw new IllegalStateException("未有欧易相关网关信息，请先创建一个欧易网关");
        }
        settings = (OkxGatewaySettings) gateway.gatewayDescription().getSettings();
        LocalTime actionTime;
        LocalDate tradingDay;

        LinkedList<Bar> barFieldList = new LinkedList<>();

        MarketDataAPIServiceImpl marketDataAPIService = new MarketDataAPIServiceImpl(config);
        JSONObject jsonObject = marketDataAPIService.getCandlesticksHistory(contract.symbol(), String.valueOf(endDate), String.valueOf(startDate), interval, "100");
        //成交量精度
        double quantityPrecision = 1 / Math.pow(10, contract.quantityPrecision());

        String data = jsonObject.getString("data");
        List<String[]> klinesList = JSON.parseArray(data, String[].class);
        for (String[] s : klinesList) {
            // 将时间戳转换为LocalDateTime对象
            Instant e = Instant.ofEpochMilli(Long.parseLong(s[0]));
            actionTime = e.atZone(ZoneId.systemDefault()).toLocalTime();
            tradingDay = e.atZone(ZoneId.systemDefault()).toLocalDate();

            double volume = Double.parseDouble(s[5]);
            double turnover = Double.parseDouble(s[7]);
            barFieldList.addFirst(Bar.builder()
                    .contract(contract)
                    .gatewayId(contract.gatewayId())
                    .tradingDay(tradingDay)
                    .actionDay(tradingDay)
                    .actionTime(actionTime)
                    .actionTimestamp(Long.parseLong(s[0]))
                    .openPrice(Double.valueOf(s[1]))
                    .highPrice(Double.valueOf(s[2]))
                    .lowPrice(Double.valueOf(s[3]))
                    .closePrice(Double.valueOf(s[4]))
                    .volume((long) volume)
                    .volumeDelta((long) volume)
                    .turnover(turnover)
                    .turnoverDelta(turnover)
                    .channelType(ChannelType.OKX)
                    .build());
        }
        return barFieldList;
    }
}
